package com.nlp.visualization.service.impl;

import com.hankcs.hanlp.HanLP;
import com.nlp.visualization.common.CONSTANTS;
import com.nlp.visualization.core.seg.ISegmentService;
import com.nlp.visualization.core.seg.SegmentType;
import com.nlp.visualization.core.seg.filter.SegFilter;
import com.nlp.visualization.pojo.NLP.seg.SegmentEntity;
import com.nlp.visualization.service.IDataSegmentService;
import com.nlp.visualization.service.tasks.NormalSegTask;
import com.nlp.visualization.utils.MD5Util;
import com.nlp.visualization.utils.PropertyUtil;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;

import static com.nlp.visualization.common.CONSTANTS.MULTI_THREAD_SIZE;

@Service
public class DataSegmentServiceImpl implements IDataSegmentService {

    @Autowired
    ISegmentService segmentService;

    Logger logger = LoggerFactory.getLogger(DataSegmentServiceImpl.class);


    /**
     * 实现正常的句子分词
     *
     * @param sentences
     * @return
     */
    public SegmentEntity seg(List<String> sentences, SegmentType segmentType, SegFilter filter) {

        if (sentences.size() > MULTI_THREAD_SIZE)
            return multiThreadSeg(sentences, segmentType, filter);
        else
            return singleThreadSeg(sentences, segmentType, filter);

    }

    /**
     * 实现单个文件分词，无需填充 SegmentEntity
     *
     * @param in
     * @param segmentType
     * @param filter
     * @return
     */
    @Override
    public File segFile(File in, String savePath, SegmentType segmentType, SegFilter filter) throws IOException {
        String commonDonwloadPath = PropertyUtil.getInstance("config").getProperty("download.common.path");
        // 检查下载目录
        File uploadDir = new File(savePath + "/" + commonDonwloadPath);
        if (!uploadDir.isDirectory()) {
            // 如果不存在，创建文件夹
            if (!uploadDir.exists()) {
                uploadDir.mkdirs();
            }
        }
        // 检查下载子目录
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMM");
        String ymd = sdf.format(new Date());
        commonDonwloadPath += "/" + ymd + "/";
        File dirFile = new File(savePath + "/" + commonDonwloadPath);
        if (!dirFile.exists()) {
            dirFile.mkdirs();
        }

        //生成一个分词后的文件
        File out = new File(savePath + "/" + commonDonwloadPath + "/" + MD5Util.MD5(in.getName()) + ".txt");

        //读取原文件，并分词，保存到out中
        String inFileString = FileUtils.readFileToString(in);
        List<String> outStringList = segmentService.executeSeg(inFileString, segmentType, filter);
        String outString = String.join(" ", outStringList);
        FileUtils.writeStringToFile(out, outString, "utf-8");

        return out;
    }

    /**
     * 实现多个文件分词，分词后保存，无需填充Segment
     *
     * @param segmentType
     * @param filter
     * @return
     */
    @Override
    public List<File> segFiles(List<File> in, String savePath, SegmentType segmentType, SegFilter filter) {
        return null;
    }

    /**
     * 直接以文本的形式返回分词结果，无需填充Segment
     *
     * @param text
     * @param segmentType
     * @param filter
     * @return
     */
    @Override
    public String segText(String text, SegmentType segmentType, SegFilter filter) {
        List<String> result = segmentService.executeSeg(text, segmentType, filter);
        return String.join(" ", result);
    }

    /**
     * 实现所有的命名实体识别的分词
     *
     * @param sentences
     * @param filter
     * @return
     */
    @Override
    public SegmentEntity segWithAllNamedEntity(List<String> sentences, SegFilter filter) {

        //填充头部
        SegmentEntity segmentEntity = new SegmentEntity();
        segmentEntity.setStatus(0);
        segmentEntity.setVersion("HanLP_STANDARD");
        for (int i = 0; i < sentences.size(); i++) {
            //声明变量
            String text = sentences.get(i);
            SegmentEntity.Results results = new SegmentEntity.Results();
            int count = 0;

            //填充结果的头部
            results.setSentenceId(i);
            results.setText(text);

            //执行分词
            List<String> termsList = segmentService.executeSegWithNamedEntity(text, filter);
            //处理分词结果
            Iterator iterator = termsList.iterator();

            while (iterator.hasNext()) {
                String term = (String) iterator.next();
                if (term.trim().equals(""))
                    continue;
                String[] terms = term.split("/");
                String word = "";
                String pos = "";
                word = terms[0];
                if (terms.length > 1)
                    pos = terms[1];
                SegmentEntity.Results.Items itemsBean = new SegmentEntity.Results.Items();
                //设置id
                itemsBean.setId(count);
                count++;
                //设置字节长度
                itemsBean.setByte_length(term.toString().getBytes().length);
                //设置词汇的标准化表达，主要针对时间、数字单位，没有归一化表达的，此项为空串
                itemsBean.setFormal("");
                //设置pos词性
                itemsBean.setPos(pos);
                //设置词汇的字符串
                itemsBean.setItem(word);
                //最细粒度的分词结果忽略
                itemsBean.setBasic_words(null);
                //添加进数组中
                results.getItems().add(itemsBean);
            }

            segmentEntity.getResults().add(results);

        }
        return segmentEntity;

    }

    /**
     * 单线程方式分词，用于数据量很少的时候
     *
     * @param sentences
     * @param segmentType
     * @param filter
     * @return
     */
    private SegmentEntity singleThreadSeg(List<String> sentences, SegmentType segmentType, SegFilter filter) {

        //填充头部
        SegmentEntity segmentEntity = new SegmentEntity();
        segmentEntity.setStatus(0);
        segmentEntity.setVersion(segmentType.toString());

        for (int i = 0; i < sentences.size(); i++) {
            //声明变量

            String text = sentences.get(i);
            SegmentEntity.Results results = new SegmentEntity.Results();
            int count = 0;

            //填充结果的头部
            results.setSentenceId(i);
            results.setText(text);

            //执行分词
            List<String> termsList = segmentService.executeSeg(text, segmentType, filter);
            //处理分词结果
            Iterator iterator = termsList.iterator();

            while (iterator.hasNext()) {
                String term = (String) iterator.next();
                if (term.trim().equals(""))
                    continue;
                String[] terms = term.split("/");
                String word = "";
                String pos = "";
                word = terms[0];
                if (terms.length > 1)
                    pos = terms[1];
                SegmentEntity.Results.Items itemsBean = new SegmentEntity.Results.Items();
                //设置id
                itemsBean.setId(count);
                count++;
                //设置字节长度
                itemsBean.setByte_length(term.toString().getBytes().length);
                //设置词汇的标准化表达，主要针对时间、数字单位，没有归一化表达的，此项为空串
                itemsBean.setFormal("");
                //设置pos词性
                itemsBean.setPos(pos);
                //设置词汇的字符串
                itemsBean.setItem(word);
                //最细粒度的分词
                List<String> baselist = new ArrayList<>();
                List<String> minTerms = segmentService.executeSeg(word, SegmentType.ANSJ_BASIC, filter);
                for (String s : minTerms) {
                    baselist.add(s.toString().split("/")[0]);
                }
                itemsBean.setBasic_words(baselist);

                //添加进数组中
                results.getItems().add(itemsBean);
            }

            segmentEntity.getResults().add(results);

        }
        return segmentEntity;

    }


    /**
     * 多线程方式分词，用于数据量巨大的时候是，这里定义的数据量巨大，在常量那里设置
     *
     * @param sentences
     * @param segmentType
     * @param filter
     * @return
     */
    private SegmentEntity multiThreadSeg(List<String> sentences, SegmentType segmentType, SegFilter filter) {
        logger.info("执行分词操作");
        long startTime = System.currentTimeMillis();
        SegmentEntity segmentEntity = new SegmentEntity();
        ConcurrentLinkedQueue<SegmentEntity.Results> queue = new ConcurrentLinkedQueue<>();
        ForkJoinPool forkJoinPool = new ForkJoinPool();

        try {
            //填充头部
            segmentEntity.setStatus(0);
            segmentEntity.setVersion(segmentType.toString());
            // 提交替换的任务
            forkJoinPool.submit(new NormalSegTask(0, sentences.size(), sentences, queue, segmentType, filter));
            forkJoinPool.shutdown();
            forkJoinPool.awaitTermination(CONSTANTS.TASK_TIMEOUT, TimeUnit.SECONDS);//阻塞当前线程直到 ForkJoinPool 中所有的任务都执行结束
            segmentEntity.getResults().addAll(queue);
        } catch (Exception e) {
            logger.error("执行多线程分词的时候异常");
            e.printStackTrace();
        }
        logger.info("完成多线程分词，共耗时" + (System.currentTimeMillis() - startTime) + "ms");
        return segmentEntity;
    }

}
